package com.qsxc.utils.redisLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RedisService {
	private static final Logger logger = LoggerFactory.getLogger(RedisService.class);

	@Autowired
	private RedisUtils redisUtils;

	private static final String SERVER_NAME = "TRAIN_REST_LOCK:";
	/**
	 * 单个业务锁最长占有时间。到达时间自动解锁
	 */
	private static final Integer MAX_LOCK_TIME = 30;

	/**
	 * 根据调用的方法和唯一键尝试获得并发锁，如果被占用则阻塞直到获取。并发锁获得后，最多持续{@link #MAX_LOCK_TIME}后自动释放
	 *
	 * @param keyStr 唯一键
	 */
	public void getlock(String keyStr) {
		try {
			String methodName = getInvokedMethodName();
			String fullKey = SERVER_NAME + methodName + keyStr;
			lock(fullKey, "1");
		}catch (Exception e){
			logger.info("redis lock 异常");
		}
	}

	/**
	 * 根据调用的方法和唯一键尝试获得并发锁，如果被占用则阻塞直到获取。并发锁获得后，最多持续{@link #MAX_LOCK_TIME}后自动释放
	 *
	 * @param keyStr 唯一键
	 */
	public void getlockFullKey(String keyStr) {
		String fullKey = SERVER_NAME + keyStr;
		lock(fullKey, "1");
	}

	/**
	 * 根据调用的方法和唯一键，在指定时间后释放并发锁
	 *
	 * @param keyStr 唯一键
	 * @param delay  指定时间
	 */
	public void releaseLockFullKeyWithDelay(String keyStr, Integer delay) {
		String fullKey = SERVER_NAME + keyStr;
		unlock(fullKey, "1", delay);
	}

	/**
	 * 判断是否已经被锁住
	 *
	 * @param keyStr
	 * @return
	 */
	public boolean findLock(String keyStr) {
		if (keyStr == null || "".equals(keyStr.trim())) {
			return false;
		}
		String methodName = getInvokedMethodName();
		String fullKey = SERVER_NAME + methodName + keyStr;
		return redisUtils.hasKey(fullKey);
	}

	/**
	 * 根据调用的方法和唯一键，在指定时间后释放并发锁
	 *
	 * @param keyStr 唯一键
	 * @param delay  指定时间
	 */
	public void releaseLockWithDelay(String keyStr, Integer delay) {
		try {
			String methodName = getInvokedMethodName();
			String fullKey = SERVER_NAME + methodName + keyStr;
			unlock(fullKey, "1", delay);
		} catch (Exception e) {
			logger.info("redis释放锁异常");
		}

	}

	public boolean hasKey(String key) {
		return redisUtils.hasKey(key);
	}

	public void del(String... key) {
		redisUtils.del(key);
	}

	private void lock(String key, String value) {
		boolean getLockFlag = false;
		int times = 0;
		while (!getLockFlag && times < MAX_LOCK_TIME) {
			try {
				// 为空则存入，返回true；否则不存入，返回false
				getLockFlag = redisUtils.setnx(key, value);
				if (getLockFlag) {
					times++;
					// 签退业务最多持有锁30秒
					redisUtils.expire(key, MAX_LOCK_TIME);
				} else {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {

					}
				}
			} catch (Exception e) {
				redisUtils.del(key);
			}
		}
	}

	private void unlock(String key, String value, Integer delay) {
		String OrgValue = (String) redisUtils.get(key);
		// 如果对应键的值匹配，则设置延迟释放锁。否则无视
		if (value.toString().equals(OrgValue)) {
			if (delay == 0) {
				redisUtils.del(key);
			} else {
				redisUtils.expire(key, delay);
			}
		}
	}

	/**
	 * 获取调用 写入日志方法的名称
	 *
	 * @return 调用{@link LogService}写入日志方法的名称
	 */
	private static String getInvokedMethodName() {
		StackTraceElement ste = Thread.currentThread().getStackTrace()[3];
		return ste.getClassName() + "#" + ste.getMethodName() + "-";
	}
}
